package wps.mojo.xmlaccess;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.resource.PlexusResource;
import org.codehaus.plexus.resource.loader.FileResourceCreationException;
import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
import org.dom4j.DocumentException;
import org.jdom.JDOMException;

import wps.mojo.utils.Dom4jUtils;
import wps.mojo.utils.FreemarkerHelper;
import wps.mojo.utils.ReflectionUtils;
import wps.mojo.utils.ResourceLocator;
import freemarker.template.TemplateException;

/**
 * @Author : <a href="mailto:Juanyong.zhang@carefirst.com">Cham
 *         Juanyong.zhang</a>
 * @Date : Apr 13, 2011
 * @goal xmlaccess
 */
public class XmlAccessMojo extends AbstractWpsMojo {

	/**
	 * Customer Mapping Configuration
	 * 
	 * @parameter expression="${mappingConfig}"
	 */
	private File mappingConfig;

	/**
	 * XML Access Templates
	 * 
	 * @parameter
	 */
	private List<String> xmlAccessTemplates;

	/**
	 * XML Access Template Directory
	 * 
	 * @parameter expression="${xmlAccessTemplateDir}"
	 */
	private File xmlAccessTemplateDir;

	/**
	 * The Freemarker Name Value Pairs defined in POM configuration.
	 * 
	 * @parameter
	 */
	private Map<String, String> nameValuePairs;
	/**
	 * The Freemarker Name Value Pairs defined in Properties file
	 * 
	 * @parameter expression="${nameValuePairsFile}"
	 */
	private File nameValuePairsFile;

	/**
	 * WebSphere portal server config URL, e.g. http://localhost:9081/wps/config
	 * 
	 * @parameter expression="${wps.config.url}"
	 */
	@FreemarkerVariable("wps.config.url")
	private String configURL;
	/**
	 * The user ID to authenticate with.
	 * 
	 * @parameter expression="${wps.user}"
	 */
	@FreemarkerVariable("wps.username")
	private String userName;
	/**
	 * The password to authenticate with.
	 * 
	 * @parameter expression="${wps.password}"
	 * @required
	 */
	@FreemarkerVariable("wps.password")
	private String password;

	/**
	 * Path of WPS installable app.
	 * 
	 * @parameter expression="${wps.installableApp}"
	 * @required
	 */
	@FreemarkerVariable("wps.installableApp")
	private String installableApp;

	/**
	 * Websphere Java home.
	 * 
	 * @parameter expression="${wps.java.home}"
	 */
	private String wpsJavaHome;

	private static final String XMLACCESS_ASSETS = "xmlaccess_assets";
	private static final String WPS_XMLACCESS_SCRIPT_IN = "wps.xmlaccess.script";
	private static final String WPS_PLUGIN_PREFIX = "wps";

	private File xmlaccessDir;
	private File xmlaccessScriptDir;
	private File xmlaccessBatchTemplate;
	@FreemarkerVariable("wps.xmlaccess.rt")
	private String xmlaccessRuntimeHome;

	private boolean customer_xa_templ_flag = false;
	private boolean customer_mapping_cfg_flag = false;

	private Properties _mappingConfig;

	private File[] _xmlAccessTemplates;
	private Map<Object, Object> _nameValuePairs;

	private void analysisPortletProject() throws Exception {

		for (Object key : _mappingConfig.keySet()) {
			String path = _mappingConfig.getProperty((String) key);
			String fn = StringUtils.split(path, "|")[0];
			String xquery = StringUtils.split(path, "|")[1];
			File dom = ResourceLocator.getOnlyFile(basedir, fn, true);
			if (null == dom) {
				throw new MojoExecutionException("File:[" + fn
						+ "] not found in path: " + basedir.getPath());
			}
			String value = Dom4jUtils.queryXML(dom, xquery);
			_nameValuePairs.put(key, value);
		}
	}

	private void copyXmlAccessAssets() throws IOException,
			ResourceNotFoundException, FileResourceCreationException {
		FileUtils.deleteQuietly(workingDirectory);
		FileUtils.forceMkdir(workingDirectory);
		PlexusResource resource = resourceManager.getResource(XMLACCESS_ASSETS);
		ResourceLocator.copyJarDirectoryToDirectory(resourceManager, resource
				.getURL().toString(), workingDirectory.getPath());
	}

	public void execute() throws MojoExecutionException, MojoFailureException {
		try {
			copyXmlAccessAssets();
			prepareVariables();
			revealSettings();
			populateProjectSetting();
			analysisPortletProject();
			generateXMLAccessScript();
			generateXMLCommandLine();
			cleanUpWorkingDirectory();
		} catch (MojoExecutionException e) {
			throw e;
		} catch (MojoFailureException e) {
			throw e;
		} catch (Exception e) {
			throw new MojoExecutionException(e.getMessage(), e);
		}
	}

	private void cleanUpWorkingDirectory() {
		if (customer_xa_templ_flag) {
			FileUtils.deleteQuietly(new File(xmlaccessDir.getPath()
					+ "/templates"));
		}
		if (customer_mapping_cfg_flag) {
			FileUtils.deleteQuietly(new File(xmlaccessDir.getPath()
					+ "/mapping.cfg.properties"));
		}
	}

	private void generateXMLAccessScript() throws FileNotFoundException,
			IOException, TemplateException, DocumentException, JDOMException {
		for (File tempalate : _xmlAccessTemplates) {
			String fileName = tempalate.getName();
			FreemarkerHelper.process(new FileInputStream(tempalate),
					_nameValuePairs,
					new FileWriter(xmlaccessScriptDir.getPath() + "/"
							+ fileName));
		}
	}

	private void generateXMLCommandLine() throws FileNotFoundException,
			IOException, TemplateException, DocumentException, JDOMException {
		Collection<File> xScripts = ResourceLocator.searchFiles(
				xmlaccessScriptDir, "xml", false, true);
		for (File xScript : xScripts) {
			_nameValuePairs.put(WPS_XMLACCESS_SCRIPT_IN, xScript.getPath());
			String batchName = StringUtils.replace(xScript.getName(), ".xml",
					".bat");

			File batchFile = new File(workingDirectory.getPath() + "/"
					+ batchName);
			FreemarkerHelper.process(
					new FileInputStream(xmlaccessBatchTemplate),
					_nameValuePairs, new FileWriter(batchFile));
			getLog().info(
					"Avaliable Command : mvn wps:execute -Dtarget=" + batchName);
		}

	}

	public String getConfigURL() {
		return configURL;
	}

	public String getInstallableApp() {
		return installableApp;
	}

	public String getPassword() {
		return password;
	}

	public String getUserName() {
		return userName;
	}

	public String getWpsJavaHome() {
		return wpsJavaHome;
	}

	public String getXmlaccessRuntimeHome() {
		return xmlaccessRuntimeHome;
	}

	private void prepareVariables() throws IOException {
		_nameValuePairs = new HashMap<Object, Object>();
		xmlaccessDir = new File(workingDirectory.getPath() + "/"
				+ XMLACCESS_ASSETS);
		xmlaccessRuntimeHome = xmlaccessDir.getPath() + "/rt";
		xmlaccessBatchTemplate = new File(xmlaccessRuntimeHome
				+ "/xmlaccess.bat");
		xmlaccessScriptDir = new File(xmlaccessDir.getPath() + "/scripts");
		FileUtils.forceMkdir(xmlaccessScriptDir);
	}

	private void populateProjectSetting() throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {

		// Populate the variables from POM
		Properties sysProps = getProperties();
		for (Object key : sysProps.keySet()) {
			if (StringUtils.startsWithIgnoreCase(key.toString(),
					WPS_PLUGIN_PREFIX)) {
				_nameValuePairs.put(key, sysProps.get(key));
			}
		}

		// Populate the variables from Mojo
		@SuppressWarnings("unchecked")
		Field[] fs = ReflectionUtils.getAllFields(this.getClass(),
				FreemarkerVariable.class);
		for (Field f : fs) {
			FreemarkerVariable fmv = f.getAnnotation(FreemarkerVariable.class);
			_nameValuePairs.put(fmv.value(),
					PropertyUtils.getProperty(this, f.getName()));
		}
	}

	private void revealMappingConfig() throws MojoFailureException, IOException {
		if (null != mappingConfig) {

			try {
				customer_mapping_cfg_flag = true;
				_mappingConfig = ResourceLocator.loadProperties(mappingConfig);
			} catch (IOException e) {
				throw new MojoFailureException(
						"nameValuePairsFile is not found :"
								+ nameValuePairsFile.getPath());
			}

		} else {
			_mappingConfig = ResourceLocator.loadProperties(xmlaccessDir
					.getPath() + "/mapping.cfg.properties");
		}
	}

	private void revealNameValuePairs() throws MojoFailureException {
		if (null != nameValuePairs) {
			_nameValuePairs.putAll(nameValuePairs);
		}
		if (null != nameValuePairsFile) {
			try {
				_nameValuePairs.putAll(ResourceLocator
						.loadProperties(nameValuePairsFile));
			} catch (IOException e) {
				throw new MojoFailureException(
						"nameValuePairsFile is not found :"
								+ nameValuePairsFile.getPath());
			}
		}
	}

	private void revealSettings() throws Exception {
		revealMappingConfig();
		revealXMLAccessTemplates();
		revealNameValuePairs();
	}

	private void revealXMLAccessTemplates() {
		List<File> templs = new ArrayList<File>();
		if (null != xmlAccessTemplates && xmlAccessTemplates.size() > 0) {
			customer_xa_templ_flag = true;

			for (String templ : xmlAccessTemplates) {
				templs.add(new File(templ));
			}
		}

		if (null != xmlAccessTemplateDir && xmlAccessTemplateDir.isDirectory()) {
			customer_xa_templ_flag = true;
			templs.addAll(ResourceLocator.searchFiles(xmlAccessTemplateDir,
					".xml", false, true));
			templs.addAll(ResourceLocator.searchFiles(xmlAccessTemplateDir,
					".ftl", false, true));
		}

		if (!customer_xa_templ_flag) {
			File locXATempDir = new File(xmlaccessDir.getPath() + "/templates");
			templs.addAll(Arrays.asList(locXATempDir.listFiles()));
		}

		_xmlAccessTemplates = templs.toArray(new File[templs.size()]);
	}

}
